package com.hhd.pdf;

import cn.hutool.core.io.FileUtil;
import com.itextpdf.text.RectangleReadOnly;
import com.itextpdf.text.pdf.PdfWriter;
import com.hhd.pdf.context.GeneratorContext;
import com.hhd.pdf.exceptions.PdfException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public abstract class AbstractPdfGenerator implements PdfGenerator {
    protected void doGenerate(GeneratorContext context) throws Exception {
        try (InputStream inputStream = loadConfig(context)) {
            Document configDocument = parseDocument(inputStream);
            Element rootElement = configDocument.getRootElement();

            parseDocumentAndGenerate(rootElement, context);
        }
    }

    public void generateWidthPageHeight(GeneratorParameters generatorParameters) throws PdfException {
        com.itextpdf.text.Document document = null;
        try {
            GeneratorContext context = createContext(generatorParameters);
            document = buildDocumentWidthPageHeight(context);
            doGenerate(context);
            saveDocument(context);
        } catch (PdfException e) {
            throw e;
        } catch (Exception e) {
            throw new PdfException(e.getMessage(), e);
        } finally {
            try {
                document.close();
            } catch (Exception e) {
            }
        }
    }

    private com.itextpdf.text.Document buildDocumentWidthPageHeight(GeneratorContext context) throws Exception {
        com.itextpdf.text.Document document = new com.itextpdf.text.Document(
                new RectangleReadOnly(context.getGeneratorParameters().getPageWidth(),
                        context.getGeneratorParameters().getPageHeight()),
                context.getGeneratorParameters().getPagePaddingLeft(),
                context.getGeneratorParameters().getPagePaddingRight(),
                context.getGeneratorParameters().getPagePaddingTop(),
                context.getGeneratorParameters().getPagePaddingBottom());
        PdfWriter.getInstance(document,
                Files.newOutputStream(Paths.get(context.getGeneratorParameters().getOutputPath())));
        document.open();
        context.setPDFdocument(document);
        return document;
    }

    @Override
    public void generate(GeneratorParameters generatorParameters) throws PdfException {
        FileUtil.mkParentDirs(generatorParameters.getOutputPath());
        com.itextpdf.text.Document document = null;
        try {
            GeneratorContext context = createContext(generatorParameters);
            document = buildDocument(context);
            doGenerate(context);
            saveDocument(context);
        } catch (PdfException e) {
            throw e;
        } catch (Exception e) {
            throw new PdfException(e.getMessage(), e);
        } finally {
            try {
                document.close();
            } catch (Exception e) {
            }
        }
        generated(generatorParameters);
    }

    private void saveDocument(GeneratorContext context) throws com.itextpdf.text.DocumentException {
        com.itextpdf.text.Document pdFdocument = context.getPDFdocument();
        for (com.itextpdf.text.Element element : context.getDocument()) {
            pdFdocument.add(element);
        }
    }

    public abstract void generated(GeneratorParameters generatorParameters);

    private void parseDocumentAndGenerate(Element rootElement, GeneratorContext context) {
        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            parseComponent(element, context);
        }
    }

    protected abstract void parseComponent(Element element, GeneratorContext context);

    private static Document parseDocument(InputStream inputStream) throws DocumentException {
        SAXReader saxReader = new SAXReader();
        return saxReader.read(inputStream);
    }

    protected com.itextpdf.text.Document buildDocument(GeneratorContext context) throws Exception {
        com.itextpdf.text.Document document = new com.itextpdf.text.Document(new RectangleReadOnly(context.getGeneratorParameters().getInitPageWidth(), context.getGeneratorParameters().getInitPageHeight()),
                0, 0, 0, 0);
        context.setPdfWriter(PdfWriter.getInstance(document, Files.newOutputStream(Paths.get(context.getGeneratorParameters().getOutputPath()))));
        document.open();
        context.setPDFdocument(document);
        return document;
    }

    protected GeneratorContext createContext(GeneratorParameters generatorParameters) {
        return GeneratorContext.builder()
                .generatorParameters(generatorParameters)
                .build()
                .init();
    }

    protected InputStream loadConfig(GeneratorContext context) {
        return this.getClass().getClassLoader().getResourceAsStream(context.getGeneratorParameters().getConfigPath());
    }
}
